<html>

  <head>
    <title>WebDABlog Admin</title>
    <script type="text/javascript" src="./js/prototype/prototype.js"></script>
    <script type="text/javascript" src="./js/webdav.js"></script>
    <script type="text/javascript" src="./js/cybouz/wikiformatter.js"></script>
    <script type="text/javascript" src="./js/trimpath/template.js"></script>
    <script type="text/javascript" src="./js/debugobj.js"></script>

    <script type="text/javascript">

function toW3CDTF(d) {
  var year = d.getFullYear();
  var mon  = d.getMonth()+1;
  var day  = d.getDate();
  var hour = d.getHours();
  var min  = d.getMinutes();
  var sec  = d.getSeconds();
  if (mon<10) mon = "0"+mon;
  if (day<10) day = "0"+day;
  if (hour<10) hour = "0"+hour;
  if (min<10) min = "0"+min;
  if (sec<10) sec = "0"+sec;

  var tzos = d.getTimezoneOffset();
  var tzhour = tzos / 60;
  var tzmin  = tzos % 60;
  var tzpm = ( tzhour > 0 ) ? "-" : "+";
  if (tzhour<0) tzhour *= -1;
  if (tzhour<10) tzhour = "0"+tzhour;
  if (tzmin<10) tzmin  = "0"+tzmin;

  var dtf = year+"-"+mon+"-"+day+"T"+hour+":"+min+":"+sec+tzpm+tzhour+":"+tzmin;
  return dtf;
}

function toEntryDateStr(d) {
  var year = d.getFullYear();
  var mon  = d.getMonth()+1;
  var day  = d.getDate();

  return year+'/'+mon+'/'+day;
}


var BlogEngine = {

  approot : location.pathname.split('/').slice(0, -1).join('/')+'/',

  _cachedPosts : {},

  loadConfig : function (callback) {
    var me = this;
    this.loadJson(
      this.approot+'config/appconfig.json',
      function(config) { 
        me.appconfig = config; 
        callback(); 
      }
    );
  }
  ,
  
  loadJson : function(path, callback) {
    var f = new File(path);
    f.load(function(file) {
      if (file) {
        var obj = eval('('+file.content+')');
        //alert(new DebuggableObject(obj));
        callback(obj);
      } else {
        callback(false);
      }
    });
  }
  ,
  
  loadPost : function(id, callback) {
    var me = this;
    if (this._cachedPosts[id]) { callback(this._cachedPosts[id]) }
    else {
      this.loadJson(this.approot+'archives/posts/'+id, function(post) {
        me._cachedPosts[id] = post;
        callback(post);
      });
    }
  }
  ,
  
  getPosts : function (options, callback) {

    function listPostFiles(next) {
      var d = new File(BlogEngine.approot+'archives/posts/');
      d.list(function(files) { next(files) });
    }

    function filterFileIds(files, next) {
      var ids = files.map(function(f) {
        return parseInt(f.href.split('/').pop());
      }).reject(function(id) {
        return (options.from && id<=options.from) || 
               (options.to && id>=options.to);
      }).sort(function (id1, id2) { 
        return options.sorting=='asc' ? id1 - id2 : id2 - id1;
      });

      if (options.limit) {
        ids = ids.slice(0, options.limit);
      }
      next(ids);
    }

    function getPosts(ids, next) {
      var posts = new Array(ids.length);
      var procs = ids.length;
      if (procs==0) next(posts);
      for (var i=0; i<ids.length; i++) {
        (function(i) {
          BlogEngine.loadPost(ids[i], function(post) {
            posts[i]=post;
            procs--;
            if (procs==0) next(posts);
          });
        })(i);
      }
    }

    // listPostFiles -> filterFileIds -> getPosts -> return(callback);
    listPostFiles(function(files) {
      if (!files) { callback(false); return; }
      filterFileIds(files, function(ids) {
        if (!ids) { callback(false); return; }
        getPosts(ids, callback)
      })
    });

  }
  ,

  loadNeighbourPosts : function(id, callback) {

    function listFiles(next) {
      var d = new File(BlogEngine.approot+'archives/posts/');
      d.list(function(files) { next(files) });
    }

    function filterFileIdSet(files, next) {
      var ids = files.map(function(f) {
        return parseInt(f.href.split('/').pop());
      }).sort(function (id1, id2) { 
        return id2 - id1;
      });
      var pivot;
      for (var i=0; i<ids.length; i++) {
        if (ids[i]==id) {
          pivot = i;
          break;
        }
      }
      
      var idset = {}
      if (pivot>0) {
        idset.next = ids[pivot-1];
      }
      if (pivot<files.length-1) {
        idset.prev = ids[pivot+1];
      }

      next(idset);
    }

    function getPostSet(idset, next) {
      var procs = 0;
      if (idset.prev) procs++;
      if (idset.next) procs++;
      var postset = {};
      if (procs==0) next(postset);
      if (idset.prev) {
        BlogEngine.loadPost(idset.prev, function(post) {
          procs--;
          postset.prev = post;
          if (procs==0) next(postset);
        });
      }
      if (idset.next) {
        BlogEngine.loadPost(idset.next, function(post) {
          procs--;
          postset.next = post;
          if (procs==0) next(postset);
        });
      }
    }

    // listPostFiles -> filter -> getPosts -> return(callback);
    listFiles(function(files) {
      if (!files) { callback(false); return; }
      filterFileIdSet(files, function(idset) {
        if (!idset) { callback(false); return; }
        getPostSet(idset, callback)
      })
    });


  }
  ,

  loadTemplate : function(name, callback) {
    var f = new File(this.approot+'template/'+name);
    f.load(function(file) {
      if (file) {
        callback(f.content);
      } else {
        callback(false);
      }
    });
  }
  ,

  savePost : function(post, callback)  {
    // disable post cache.
    if (this._cachedPosts[post.id]) delete this._cachedPosts[post.id];

    var f = new File(BlogEngine.approot+'archives/posts/'+post.id);
    f.content = new DebuggableObject(post).toString();
    f.save(callback);
  }
  ,

  removePost : function(id, callback) {
    var postset;

    function loadNeighbours(next) {
      BlogEngine.loadNeighbourPosts(id, function(ps) {
        postset = ps;
        next();
      });
    }

    function deleteEntry(next) {
      var procs = 2;
      var f1 = new File(BlogEngine.approot+'archives/posts/'+id);
      var f2 = new File(BlogEngine.appconfig.publicRoot+id+'.html');
      f1.remove(function(){
        procs--;
        if (procs==0) next();
      });
      f2.remove(function(){
        procs--;
        if (procs==0) next();
      });
    }

    function rebuildNeighbours(next) {
      var procs = 2;
      if (postset.next) {
        BlogEngine.rebuildPost(postset.next.id, notify)
      } else { 
        notify();
      }
      if (postset.prev) {
        BlogEngine.rebuildPost(postset.prev.id, notify)
      } else {
        notify();
      }

      function notify() { 
        procs--; 
        if (procs==0) next();
      }
    }

    // disable post cache.
    if (this._cachedPosts[id]) delete this._cachedPosts[id];

    loadNeighbours(function() {
      deleteEntry(function() {
        rebuildNeighbours(callback);
      });
    });
  }
  ,

  rebuildPost : function (id, callback) {
    var template;
    var context = { appconfig : this.appconfig };
    function loadData(next) {
      var proc=4;
      BlogEngine.loadTemplate('entry.html.template', function(thtml) {
        template = TrimPath.parseTemplate(thtml);
        proc--;
        if (proc==0) next();
      });
      BlogEngine.getPosts({ limit : 5 }, function(posts) {
        context.recentPosts = posts;
        proc--;
        if (proc==0) next();
      });
      BlogEngine.loadPost(id, function(post) {
        post.html = new Wiki.Formatter().format(post.text);
        context.entry = post;
        proc--;
        if (proc==0) next();
      });
      BlogEngine.loadNeighbourPosts(id, function(postset) {
        context.prev = postset.prev;
        context.next = postset.next;
        proc--;
        if (proc==0) next();
      });
    }

    function renderHtml(next) {
      var html = template.process(context);
      var f = new File(context.appconfig.outputDir+id+'.html');
      f.contentType = 'text/html';
      f.content = html;
      f.save(next);
    }

    loadData(function() {
      renderHtml(callback)
    });

  }
  ,
  
  rebuildIndex : function (callback) {
    var template = {};
    var context = { appconfig : this.appconfig };

    function loadData(next) {
      var proc=3;
      BlogEngine.loadTemplate('index.html.template', function(thtml) {
        template.html = TrimPath.parseTemplate(thtml);
        proc--;
        if (proc==0) next();
      });
      BlogEngine.loadTemplate('atom.xml.template', function(tatom) {
        template.atom = TrimPath.parseTemplate(tatom);
        proc--;
        if (proc==0) next();
      });
      BlogEngine.getPosts({ limit : 5 }, function(posts) {
        for (var i=0; i<posts.length; i++) {
          posts[i].html = new Wiki.Formatter().format(posts[i].text);
        }
        context.recentPosts = posts;
        proc--;
        if (proc==0) next();
      });
    }

    function renderHtml(next) {
      console.log(context);
      var html = template.html.process(context);
      var atom = template.atom.process(context);
      var f1 = new File(context.appconfig.outputDir+'index.html');
      var f2 = new File(context.appconfig.outputDir+'atom.xml');
      f1.contentType = 'text/html';
      f1.content = html;
      f2.contentType = 'text/xml';
      f2.content = atom;
      var procs = 2;
      f1.save(function(f) {
        procs--;
        if (procs==0) next(true);
      });
      f2.save(function(f) {
        procs--;
        if (procs==0) next(true);
      });
    }

    loadData(function() {
      renderHtml(callback)
    });

  }
  ,

  rebuildAll : function(callback) {

    function loadAllPosts(next) {
      BlogEngine.getPosts({}, function(posts) {
        var formatter = new Wiki.Formatter();
        for (var i=0; i<posts.length; i++) {
          posts[i].html = formatter.format(posts[i].text);
        }
        next(posts);
      });
    }

    function rebuildPosts(posts, next) {
      var procs = posts.length;
      if (procs==0) next();
      for (var i=0; i<posts.length; i++) {
        BlogEngine.rebuildPost(posts[i].id, function() {
          procs--;
          if (procs==0) next();
        });
      }
    }

    loadAllPosts(function(posts) {
      rebuildPosts(posts, callback);
    });

  }

}



function listPosts(pivot) {
  $('list-loading-img').style.display='inline';
  $('list-box').innerHTML = '';

  var pivotIndex = 4;
  if (pivot) {
    var posts = [];
    BlogEngine.loadPost(pivot, function(post) {
      posts.push(post);

      var procs = 2;
      var start = 2;
      BlogEngine.getPosts({limit : 4, sorting : 'asc', from : pivot } , function(nposts) {
        pivotIndex = nposts.length;
        posts = nposts.reverse().concat(posts);
        procs--;
        if (procs==0) processPosts(posts);
      });
      BlogEngine.getPosts({limit : 4, sorting : 'desc', to : pivot } , function(pposts) {
        posts = posts.concat(pposts);
        procs--;
        if (procs==0) processPosts(posts);
      });
    });

  } else {
    BlogEngine.getPosts({limit : 5} , processPosts);
  }

  function processPosts(posts) {
    if (pivotIndex>posts.length-3) pivotIndex=posts.length-3;
    if (pivotIndex<2) pivotIndex=2;
    posts = posts.slice(pivotIndex-2, pivotIndex+3);
    var template = TrimPath.parseTemplate($('list-box-template').value);
    $('list-loading-img').style.display='none';
    $('list-box').innerHTML = template.process({ posts : posts });
  }
}

function disableInputs() {
  $('backToButton').disabled=true;
  $('previewButton').disabled=true;
  $('saveButton').disabled=true;
  $('deleteButton').disabled=true;
  $('title').disabled=true;
  $('text').disabled=true;
}

function enableInputs() {
  $('backToButton').disabled=false;
  $('previewButton').disabled=false;
  $('saveButton').disabled=false;
  $('deleteButton').disabled=false;
  $('title').disabled=false;
  $('text').disabled=false;
}

function rebuildPost() {
  $('loading-img').style.display='inline';
  disableInputs();

  var id = $('id').value;
  showMessage('Rebuilding entry...');
  BlogEngine.rebuildPost(id, function() {
    showMessage('Rebuilding indexes...');
    BlogEngine.rebuildIndex(function() {
      showMessage('Rebuild complete.');
      $('loading-img').style.display='none';
      enableInputs();
      setTimeout(function() { listPosts(id) }, 500);
      backToEdit();
    });
  });

}

function savePost() {

  $('loading-img').style.display='inline';
  disableInputs();

  var now = new Date().getTime();
  var post = {
    title : $('title').value,
    text : $('text').value
  }
  if ($('id').value.length>0) { 
    post.id = $('id').value 
  } else { 
    post.id = post.id || now 
  }

  showMessage('Saving entry...');
  BlogEngine.loadPost(post.id, function(origPost) {
    post.created = origPost && origPost.created || now;
    post.updated = now;
    BlogEngine.savePost(post, function() {
      showMessage('Saved. <a href="javascript:rebuildPost()">Rebuild entry ?</a>');
      location.hash = post.id;
      $('id').value = post.id;
      $('loading-img').style.display='none';
      enableInputs();
      setTimeout(function() { listPosts(post.id) }, 500);
      backToEdit();
    });
  });

}

function preview() {
  clearMessage();
  $('backToButton').style.display='inline';
  $('previewButton').style.display='none';

  $('body-preview-box').innerHTML = new Wiki.Formatter().format($('text').value)
  
  $('body-preview-box').style.display='block';
  $('body-box').style.display='none';
}

function backToEdit() {
  //clearMessage();
  $('backToButton').style.display='none';
  $('previewButton').style.display='inline';

  $('body-preview-box').style.display='none';
  $('body-box').style.display='block';
}

function loadPost() {
  clearMessage();
  if (location.href.indexOf('#')>0) {
    var id = location.href.replace(/^.*#/, '');
    BlogEngine.loadPost(id, function(post){
      if (post) {
        $('id').value = post.id;
        $('title').value = post.title;
        $('text').value = post.text;
        $('created').value = post.created;
        $('deleteButton').style.display='inline';
        enableInputs();
        setTimeout(function() { listPosts(post.id) }, 500);
      } else {
        showMessage('No post found : '+id);
      }
    });
  }
}

function removePost() {
  var id = $('id').value;
  if (id && confirm('Are you sure to delete this post ?')) {
    disableInputs();
    showMessage('Deleting post...');
    BlogEngine.removePost(id, function() {
      showMessage('Post deleted.');
      enableInputs();
      setTimeout(listPosts, 500);
      newPost();
    });
  }
}

function newPost() {
  clearMessage();
  location.hash='#';
  $('id').value = '';
  $('title').value = '';
  $('text').value = '';
  $('created').value = '';
  $('title').focus();
  $('deleteButton').style.display='none';
}

function rebuildAll() {
  if (confirm('Rebuilding all posts will take a lot of time. '+
              ' Are you sure to rebuild all entries ?')) {
    disableInputs();
    showMessage('Rebuilding all posts..');
    BlogEngine.rebuildAll(function() {
      showMessage('Rebuilding indexes...');
      BlogEngine.rebuildIndex(function() {
        enableInputs();
        showMessage('Rebuild completed');
      });
    });
  }
}

function showMessage(str) {
  $('message-box').innerHTML = str;
  $('message-box').style.display = 'block';
}

function clearMessage(str) {
  $('message-box').innerHTML = '';
  $('message-box').style.display = 'none';
}

window.onload = function() {
  BlogEngine.loadConfig(function() {
    listPosts();
    loadPost();
    $('siteLink').href = BlogEngine.appconfig.publicRoot;
  });
}
    </script>
    <style type="text/css">
a {
  color : #f88;
  text-decoration:none;
}
a:hover {
  text-decoration:underline;
}
a.viewLink {
  font-size : x-small;
  font-weight : bold;
  color : #004;
}
#page {
  width : 700px;
  background-color : #ffe;
}
#header {
  margin : 0px;
  padding : 10px 30px;
  font-weight: bold;
  font-size : x-large;
  color : #00a;
  background-color : #ccf;
  border : 1px solid #aaf;
}
#main {
  margin : 0px;
  padding : 0px;
}
#sidebar {
  margin : 0px;
  padding : 10px;
  float : left;
  width : 180px;
  height : 500px;
  background-color : #eef;
}
#content #content-inner {
  margin : 0px;
  padding : 10px;
  width : 480px;
  float : right;
}
#footer {
  clear : both;
}
.menu .menu-header {
  color : #447;
  font-weight : bold;
  font-size : large;
}
.menu .menu-header a {
  color : #447;
}
#message-box, #error-box {
  margin-top : 1px;
  margin-bottom : 10px;
  padding : 10px;
  color : #440;
}
#message-box {
  background-color : #ee0;
  border : 1px solid #990;
}
#error-box {
  background-color : #f88;
  border : 1px solid #fcc;
}
#list-box ul {
  margin : 0px;
  padding-left : 20px;
}
#list-box ul li{
  padding : 3px 0px;
}
textarea {
  width : 480px;
  height : 300px;
}
input.titleInput {
  width : 480px;
}
    </style>
  </head>
  <body>

<div id="page">


<div id="header">
  WebDABlog Admin 
  <span style="font-size:smaller"><a id="siteLink" class="viewLink" href="#">(view site)</a></span>
</div>
  
<div id="main">
  <div id="sidebar">
    <div id="sidbar-inner">

      <div class="menu">
        <div class="menu-header">
          <a href="javascript:newPost()">New Post</a>
        </div>
      </div>

      <div class="menu">
        <div class="menu-header">
          <a href="javascript:rebuildAll()">Rebuild All Posts</a>
        </div>
      </div>

      <div class="menu">
        <div class="menu-header">
          Posted Entries
        </div>
        <img id="list-loading-img" src="images/loading.gif" style="display:none">
        <div class="menu-content" id="list-box">
        </div>
        <textarea id="list-box-template" style="display:none">
<ul>
{for p in posts}
  <li>
    <a href="#${p.id}" onclick="setTimeout(loadPost, 100)">${p.title}</a>
    <a class="viewLink" href="${BlogEngine.appconfig.publicRoot}${p.id}.html">(view entry)</a>
  </li>
{/for}
</ul>
        </textarea>
      </div>

    </div>
  </div>

  <div id="content">
    <div id="content-inner">
      <div id="message-box" style="display:none;"></div>
      <div id="error-box" style="display:none;"></div>
      <div id="title-box">
        <input type="hidden" id="id">
        <input type="hidden" id="created">
        <input type="text" class="titleInput" id="title">
      </div>
      <div id="body-box">
        <textarea id="text"></textarea>
      </div>
      <div id="body-preview-box" style="display:none">
      </div>
      <div id="list-actions">
        <input type="button" id="saveButton" value="Save" onclick="savePost()">
        <input type="button" id="previewButton" value="Preview" onclick="preview()">
        <input type="button" id="backToButton" style="display:none" value="Back" onclick="backToEdit()">
        <input type="button" id="deleteButton" style="display:none" value="Delete" onclick="removePost()">
        <img id="loading-img" src="images/loading.gif" style="display:none">
      </div>
    </div>
  </div>

</div>

<div id="footer">
</div>

</div>
  </body>
</html>
